import {
  PipeTransform,
  Injectable,
  ArgumentMetadata,
  BadRequestException,
} from '@nestjs/common';
import { validate, ValidationError } from 'class-validator';
import { plainToClass } from 'class-transformer';

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value: any, { metatype }: ArgumentMetadata): Promise<any> {
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }

    // 处理字段名转换
    const transformedValue = { ...(value as Record<string, any>) };
    if (transformedValue.frequencyRule && !transformedValue.frequency_rule) {
      transformedValue.frequency_rule =
        transformedValue.frequencyRule as string;
      delete transformedValue.frequencyRule;
    }

    const object = plainToClass(metatype, transformedValue, {
      enableImplicitConversion: true,
      excludeExtraneousValues: false,
    }) as object;
    const errors = await validate(object, {
      whitelist: true, // 自动移除不在DTO中定义的属性
      forbidNonWhitelisted: true, // 如果存在非白名单属性则抛出错误
    });

    if (errors.length > 0) {
      const errorMessages = this.formatErrors(errors);
      throw new BadRequestException({
        message: errorMessages,
        error: 'Validation Error',
        statusCode: 400,
      });
    }

    return object;
  }

  private toValidate(metatype: new (...args: any[]) => any): boolean {
    const types: (new (...args: any[]) => any)[] = [
      String,
      Boolean,
      Number,
      Array,
      Object,
    ];
    return !types.includes(metatype);
  }

  private formatErrors(errors: ValidationError[]): string[] {
    const errorMessages: string[] = [];

    errors.forEach((error) => {
      if (error.constraints) {
        Object.values(error.constraints).forEach((constraint: string) => {
          errorMessages.push(constraint);
        });
      }

      // 处理嵌套验证错误
      if (error.children && error.children.length > 0) {
        const childErrors = this.formatErrors(error.children);
        errorMessages.push(...childErrors);
      }
    });

    return errorMessages;
  }
}
